#include "gtest/gtest.h"
#include <acapella/delegate/delegate.hpp>


int hello(int i)
{
    return i + 1;
}

int goodbye(int i)
{
    return i + i;
}

class ADelegate
{
public:
    virtual ~ADelegate(){}

    virtual int hello(int i)
    {
        return i + 1;
    }

    virtual int goodbye(int i)
    {
        return i + 1;
    }

    virtual ADelegate* complex(const ADelegate&, acapella::shared_ptr<ADelegate>)
    {
        return new ADelegate;
    }
};

class BDelegate : public ADelegate
{
public:
    virtual ~BDelegate(){}

    virtual int hello(int i)
    {
        return i + 2;
    }

    virtual int goodbye(int i)
    {
        return i + 2;
    }
};

using namespace acapella::delegate;


TEST(DelegateTest, NormalDynamicDelegateTest)
{
    delegate<int(int)> dele(delegate<int(int)>::functor_type(hello), 1);
    EXPECT_EQ(2, dele());
}

TEST(DelegateTest, MemeberDynamicDelegateTest)
{
    ADelegate test;

    delegate<int(int)> dele(delegate<int(int)>::functor_type(&test, &ADelegate::hello), 1);
    EXPECT_EQ(2, dele());
}

TEST(DelegateTest, ComplexDelegateTest)
{
    typedef ADelegate*(FuncType)(const ADelegate&, acapella::shared_ptr<ADelegate>);

    acapella::shared_ptr<ADelegate> p(new BDelegate);

    delegate<FuncType> dele(delegate<FuncType>::functor_type(p, &ADelegate::complex),  *p, p);
    EXPECT_NE(p, dele());
}

TEST(DelegateTest, NormalStaticDelegateTest)
{
    delegate<int(int), static_bind> dele(delegate<int(int), static_bind>::functor_type(hello), 1);
    EXPECT_EQ(2, dele());
}

TEST(DelegateTest, MemberStaticDelegateTest)
{
    ADelegate test;

    delegate<int(int), static_bind, ADelegate> dele
        (delegate<int(int), static_bind, ADelegate>::functor_type(&test, &ADelegate::goodbye), 1);
    EXPECT_EQ(2, dele());
}

TEST(DelegateTest, DynamicBenchmarkTest)
{
    delegate<int(int)> dele(delegate<int(int)>::functor_type(&hello), 1);

    int i = 1000000;
    int sum = 0;
    while (i--)
    {
        sum += dele();
    }
}


TEST(DelegateTest, StaticBenchmarkTest)
{
    delegate<int(int), static_bind> dele(delegate<int(int), static_bind>::functor_type(&hello), 1);

    int i = 1000000;
    int sum = 0;
    while (i--)
    {
        sum += dele();
    }
}

TEST(DelegateTest, NormalBenchmarkTest)
{
    int i = 1000000;
    int sum = 0;
    while (i--)
    {
        sum += hello(1);
    }
}



TEST(DelegateListTest, NormalDynamicTest)
{
    typedef int(FuncType)(int);

    acapella::shared_ptr<ADelegate> p(new BDelegate);

    delegate_list<FuncType> dele(delegate<FuncType>::functor_type(p, &ADelegate::goodbye), 1);

    dele.getFunctor() += delegate<FuncType>::functor_type(p, &ADelegate::goodbye);

    EXPECT_EQ(3, dele());
}

